﻿using System;
using System.Collections.Generic;
using BlobNet.Base;
using BlobNet.Base.Collections;
using BlobNet.Base.Enums;
using BlobNet.FunctionalTests.Dummy;
using BlobNet.TestsCommon.DataProviderHelpers;
using BlobNet.TestsCommon.DataProviderHelpers.Base;
using BlobNet.TestsCommon.Utils;
using NUnit.Framework;

namespace BlobNet.FunctionalTests
{
    internal class FunctionalTestHelper
    {
        private readonly DataProviderTestHelper _dataProviderTestHelper;

        private const string StringTestValueBlobDomain = "Test Value BlobDomain";
        private const string StringTestValueDummyBlobPartA = "Test Value DummyBlobPartA";
        private const string StringTestValueDummyBlobPartB = "Test Value DummyBlobPartB";
        private const string ObjectProperty1TestValue = "test";
        public const int BlobPartAItems = 2;

        public FunctionalTestHelper(DataProviderTestHelper dataProviderTestHelper)
        {
            if (dataProviderTestHelper == null) throw new ArgumentNullException("dataProviderTestHelper");
            _dataProviderTestHelper = dataProviderTestHelper;
        }


        public DummyBlobDomainHolder CreatePopulatedBlobDomainHolder(bool autoAttachObjects,bool asPersistent=false)
        {
            var result = new DummyBlobDomainHolder(Guid.NewGuid());
            result.StatusStorage = asPersistent ? StatusStorageType.Persistent : StatusStorageType.Memory;

            result.StringProperty = StringTestValueBlobDomain;

            result.IntProperty = int.MaxValue;
            
            result.BlobPartB = CreateBlobPartB(result, autoAttachObjects);

            result.CollectionProperty = GetBlobPartACollection(result, autoAttachObjects);

            return result;
        }

     

        public void CheckDefaultBlobDomainHolderContent(DummyBlobDomainHolder blobDomain, bool dettachedCase)
        {
            Assert.IsNotNull(blobDomain);
            Assert.IsTrue(blobDomain.Domain == blobDomain);
            Assert.AreEqual(blobDomain.StringProperty, StringTestValueBlobDomain);
            Assert.AreEqual(blobDomain.IntProperty, int.MaxValue);

            CheckDefaultCollectionA(blobDomain.CollectionProperty, blobDomain, dettachedCase);
        }

        public  void CheckDefaultBlobDomainHolderDataProviderContent(DummyBlobDomainHolder blobDomain,
                                                                    bool checkDbDataExists, bool dettachedCase)
        {
            CheckDefaultBlobDomainHolderContent(blobDomain, dettachedCase);

            CheckDbBlobDomainHolderRows(blobDomain, checkDbDataExists);

            CheckDbBlobDomainHolderCollectionA(blobDomain.CollectionProperty, checkDbDataExists, dettachedCase);
        }

        public  void CheckDbBlobDomainHolderCollectionA(IList<DummyBlobPartA> collection, bool checkExists,
                                                           bool dettachedCase)
        {
            Assert.IsTrue(collection.GetType().Name == "BlobList`1");//TODO: BETTER CHECKING OF THIS

            if (collection.Count > BlobPartAItems ||
                ((BlobList<DummyBlobPartA>) collection).Domain.StatusStorage == StatusStorageType.Persistent)
                CheckCollectionItemsExistsInDb((BlobList<DummyBlobPartA>) collection, checkExists);

            foreach (DummyBlobPartA item in collection)
            {
                CheckDbBlobDomainHolderCollectionB(item.CollectionProperty, checkExists, dettachedCase);

                if (checkExists)
                {
                    if (!dettachedCase &&
                        (collection.Count > BlobPartAItems ||
                         item.Domain.StatusStorage == StatusStorageType.Persistent))
                        CheckDbRowsBlobPartA(item, checkExists);
                }
                else
                {
                    Dictionary<string, object> retrieval = _dataProviderTestHelper.GetEntitiesStatusRow(
                        item.Domain.EntityId, item.EntityId, "StringProperty");
                    Assert.IsTrue(retrieval.Count == 0);
                }
            }
        }

        public  void CheckCollectionItemsExistsInDb(BlobList<DummyBlobPartA> collection, bool checkExists)
        {
            for (int i = 0; i < collection.Count; i++)
            {
                if (checkExists)
                {
                    Dictionary<string, object> row = _dataProviderTestHelper.GetListEntitiesRow(
                        collection.Domain.EntityId, collection.EntityId, i);

                    Assert.IsNotNull(row);
                    Assert.AreEqual(row["EntityId"].ToString(), collection[i].EntityId.ToString());
                    Assert.AreEqual(row["FullyQualifiedType"].ToString(), typeof (DummyBlobPartA).FullName);
                }
                else
                {
                    if (collection.Domain != null)
                    {
                        Dictionary<string, object> row =
                            _dataProviderTestHelper.GetListEntitiesRow(collection.Domain.EntityId, collection.EntityId, i);
                        Assert.IsTrue(row.Count == 0);
                    }
                }
            }
        }


        public  void CheckDbBlobDomainHolderCollectionB(IList<DummyBlobPartB> collection, bool checkExists,
                                                           bool dettachedCase)
        {
            Assert.IsTrue(collection.GetType().Name == "BlobList`1");//TODO: BETTER CHECKING OF THIS

            //VAR doCheck = checkExists || collection.Count > BlobPartAItems ||
            //               ((BlobListWrapper<DummyBlobPartB>) collection).BlobDomain.StatusStorage ==StatusStorageType.DataProvider;

            CheckCollectionItemsExistsInDbB((BlobList<DummyBlobPartB>) collection, checkExists, dettachedCase);

            foreach (DummyBlobPartB item in collection)
            {
                if (!dettachedCase)
                    CheckDbRowsBlobPartB(item, checkExists);
            }
        }

        public  void CheckCollectionItemsExistsInDbB(BlobList<DummyBlobPartB> collection, bool checkExists,
                                                           bool dettachedCase)
        {
            for (int i = 0; i < collection.Count; i++)
            {
                if (!dettachedCase)
                {
                    if (checkExists)
                    {
                        Dictionary<string, object> row =
                            _dataProviderTestHelper.GetListEntitiesRow(collection.Domain.EntityId,
                                                                   collection.EntityId, i);
                        Assert.IsNotNull(row);
                        Assert.AreEqual(row["EntityId"].ToString(), collection[i].EntityId.ToString());
                        Assert.AreEqual(row["FullyQualifiedType"].ToString(), typeof (DummyBlobPartB).FullName);
                    }
                    else
                    {
                        Assert.IsNotNull(collection.Domain);
                        Dictionary<string, object> row =
                            _dataProviderTestHelper.GetListEntitiesRow(collection.Domain.EntityId,
                                                                   collection.EntityId, i);
                        Assert.IsTrue(row.Count == 0);
                    }
                }
            }
        }


        public void CheckDbRowsBlobPartA(DummyBlobPartA item, bool checkExists)
        {
            Assert.IsNotNull(item);

            if (checkExists)
            {
                Dictionary<string, object> retrieval = _dataProviderTestHelper.GetEntitiesStatusRow(
                    item.Domain.EntityId, item.EntityId, "StringProperty");
                Assert.IsTrue(retrieval.Count > 0);
                Assert.AreEqual(retrieval["Value"].ToString(), StringTestValueDummyBlobPartA);

                retrieval = _dataProviderTestHelper.GetEntitiesStatusRow(item.Domain.EntityId, item.EntityId,
                                                                     "IntProperty");
                Assert.IsTrue(retrieval.Count > 0);
                Assert.AreEqual(int.Parse(retrieval["Value"].ToString()), int.MaxValue);

                retrieval = _dataProviderTestHelper.GetEntitiesStatusRow(item.Domain.EntityId, item.EntityId,
                                                                     "BlobPartB");
                Assert.IsTrue(retrieval.Count > 0);

                var dummyObject = ((DummyBlobPartB)retrieval["Value"]);
                Assert.IsNotNull(dummyObject);
                Assert.AreEqual(dummyObject.StringProperty, ObjectProperty1TestValue);
            }
            else
            {
                Dictionary<string, object> retrieval = _dataProviderTestHelper.GetEntitiesStatusRow(
                    item.Domain.EntityId, item.EntityId, "StringProperty");
                Assert.IsTrue(retrieval.Count == 0);

                retrieval = _dataProviderTestHelper.GetEntitiesStatusRow(item.Domain.EntityId, item.EntityId,
                                                                     "IntProperty");
                Assert.IsTrue(retrieval.Count == 0);

                retrieval = _dataProviderTestHelper.GetEntitiesStatusRow(item.Domain.EntityId, item.EntityId,
                                                                     "ObjectProperty");
                Assert.IsTrue(retrieval.Count == 0);
            }
        }

        public  void CheckDbRowsBlobPartB(DummyBlobPartB item, bool checkExists)
        {
            Assert.IsNotNull(item);
            if (checkExists)
            {
                Dictionary<string, object> retrieval = _dataProviderTestHelper.GetEntitiesStatusRow(
                    item.Domain.EntityId, item.EntityId, "StringProperty");
                Assert.IsTrue(retrieval.Count > 0);
                Assert.AreEqual(retrieval["Value"].ToString(), StringTestValueDummyBlobPartB);

                retrieval = _dataProviderTestHelper.GetEntitiesStatusRow(item.Domain.EntityId, item.EntityId,
                                                                     "IntProperty");
                Assert.IsTrue(retrieval.Count > 0);
                Assert.AreEqual(int.Parse(retrieval["Value"].ToString()), int.MaxValue);

               
            }
            else
            {
                Dictionary<string, object> retrieval = _dataProviderTestHelper.GetEntitiesStatusRow(
                    item.Domain.EntityId, item.EntityId, "StringProperty");
                Assert.IsTrue(retrieval.Count == 0);

                retrieval = _dataProviderTestHelper.GetEntitiesStatusRow(item.Domain.EntityId, item.EntityId,
                                                                     "IntProperty");
                Assert.IsTrue(retrieval.Count == 0);
                
            }
        }


        public  void CheckDbBlobDomainHolderRows(DummyBlobDomainHolder item, bool checkDbDataExists)
        {
            Assert.IsNotNull(item);
            if (checkDbDataExists)
            {
                Dictionary<string, object> retrieval = _dataProviderTestHelper.GetEntitiesStatusRow(
                    item.Domain.EntityId, item.EntityId, "StringProperty");
                Assert.IsTrue(retrieval.Count > 0);
                Assert.AreEqual(retrieval["Value"].ToString(), StringTestValueBlobDomain);

                retrieval = _dataProviderTestHelper.GetEntitiesStatusRow(item.Domain.EntityId, item.EntityId,
                                                                     "IntProperty");
                Assert.IsTrue(retrieval.Count > 0);
                Assert.AreEqual(int.Parse(retrieval["Value"].ToString()), int.MaxValue);

                retrieval = _dataProviderTestHelper.GetEntitiesStatusRow(item.Domain.EntityId, item.EntityId,
                                                                     "BlobPartB");
                Assert.IsTrue(retrieval.Count > 0);
                var dummyObject = ((DummyBlobPartB) retrieval["Value"]);
                Assert.IsNotNull(dummyObject);
                Assert.AreEqual(dummyObject.StringProperty, ObjectProperty1TestValue);
            }
            else
            {
                Dictionary<string, object> retrieval = _dataProviderTestHelper.GetEntitiesStatusRow(
                    item.Domain.EntityId, item.EntityId, "StringProperty");
                Assert.IsTrue(retrieval.Count == 0);

                retrieval = _dataProviderTestHelper.GetEntitiesStatusRow(item.Domain.EntityId, item.EntityId,
                                                                     "IntProperty");
                Assert.IsTrue(retrieval.Count == 0);

                retrieval = _dataProviderTestHelper.GetEntitiesStatusRow(item.Domain.EntityId, item.EntityId,
                                                                     "ObjectProperty");
                Assert.IsTrue(retrieval.Count == 0);
            }
        }

        public  void CheckDefaultListDumpedInDataProviderContent(DummyBlobDomainHolder blobDomain, bool dettachedCase)
        {
            CheckDefaultListDumpedInDataProviderContent(blobDomain, true, dettachedCase);
        }

        public  void CheckDefaultListDumpedInDataProviderContent(DummyBlobDomainHolder blobDomain, bool checkExists,
                                                                   bool dettachedCase)
        {
            CheckDbBlobDomainHolderCollectionA(blobDomain.CollectionProperty, checkExists, dettachedCase);
        }


        public  BlobList<DummyBlobPartA> GetBlobPartACollection(BlobDomain blobDomain,
                                                                               bool autoAttachObjects)
        {
            var result = autoAttachObjects? new BlobList<DummyBlobPartA>(blobDomain) : new BlobList<DummyBlobPartA>();

            for (int i = 0; i < BlobPartAItems; i++)
                result.Add(CreateBlobPartA(blobDomain, autoAttachObjects));

            return result;
        }

        public  BlobList<DummyBlobPartB> GetBlobPartBCollection(BlobDomain blobDomain,
                                                                               bool autoAttachObjects)
        {
            var result = autoAttachObjects ? new BlobList<DummyBlobPartB>(blobDomain) : new BlobList<DummyBlobPartB>();

            result.Add(CreateBlobPartB(blobDomain, autoAttachObjects));
            result.Add(CreateBlobPartB(blobDomain, autoAttachObjects));
            result.Add(CreateBlobPartB(blobDomain, autoAttachObjects));

            return result;
        }

        public  DummyBlobPartA CreateBlobPartA(BlobDomain blobDomain, bool autoAttachObjects)
        {
            DummyBlobPartA result = autoAttachObjects
                                              ? new DummyBlobPartA(blobDomain)
                                              : new DummyBlobPartA();

            result.StringProperty = StringTestValueDummyBlobPartA;

            result.IntProperty = int.MaxValue;

            result.BlobPartB = CreateBlobPartB(blobDomain, autoAttachObjects);

            result.CollectionProperty = GetBlobPartBCollection(blobDomain, autoAttachObjects);


            return result;
        }


        public  DummyBlobPartB CreateBlobPartB(BlobDomain blobDomain, bool autoAttachObjects)
        {
            DummyBlobPartB result = autoAttachObjects
                                              ? new DummyBlobPartB(blobDomain)
                                              : new DummyBlobPartB();

            result.StringProperty = StringTestValueDummyBlobPartB;

            result.IntProperty = int.MaxValue;


            return result;
        }

        public  void CheckDefaultCollectionA(IList<DummyBlobPartA> collection, BlobDomain blobDomain,
                                                   bool dettachedCase)
        {
            Assert.IsNotNull(collection);
            Assert.IsTrue(collection.GetType().Name == "BlobList`1");//TODO: BETTER CHECKING OF THIS

            BlobDomain aBlobDomain = ((BlobList<DummyBlobPartA>) collection).Domain;
            if (!dettachedCase)
            {
                Assert.IsNotNull(blobDomain);
                Assert.IsTrue(aBlobDomain.EntityId == blobDomain.EntityId);
            }
            Assert.IsTrue(collection.Count == 2);

            foreach (DummyBlobPartA item in collection)
            {
                if (!dettachedCase)
                    Assert.IsTrue(item.Domain.EntityId == blobDomain.EntityId);
                Assert.AreEqual(item.StringProperty, StringTestValueDummyBlobPartA);
                Assert.AreEqual(item.IntProperty, int.MaxValue);
                CheckDefaultCollectionB(item.CollectionProperty, blobDomain, dettachedCase);
            }
        }

        public  void CheckDefaultCollectionB(IList<DummyBlobPartB> collection, BlobDomain blobDomain,
                                                   bool dettachedCase)
        {
            Assert.IsNotNull(collection);
            Assert.IsTrue(collection.GetType().Name == "BlobList`1");//TODO: BETTER CHECKING OF THIS
            BlobDomain aBlobDomain = ((BlobList<DummyBlobPartB>)collection).Domain;
            if (!dettachedCase)
            {
                Assert.IsNotNull(blobDomain);
                Assert.IsTrue(aBlobDomain.Domain.EntityId == blobDomain.EntityId);
            }
            Assert.IsTrue(collection.Count == 3);

            foreach (DummyBlobPartB item in collection)
            {
                if (!dettachedCase)
                    Assert.IsTrue(item.Domain.EntityId == blobDomain.EntityId);
                Assert.AreEqual(item.StringProperty, StringTestValueDummyBlobPartB);
                Assert.AreEqual(item.IntProperty, int.MaxValue);
            }
        }

        public  void CheckBlobDomainAreEqual(DummyBlobDomainHolder expected, DummyBlobDomainHolder actual)
        {
            Assert.AreEqual(expected.EntityId, actual.EntityId);
            Assert.AreEqual(expected.StatusStorage, actual.StatusStorage);
            Assert.AreEqual(expected.IntProperty, actual.IntProperty);
            Assert.AreEqual(expected.StringProperty, actual.StringProperty);
            CheckBlobPartBAreEqual(expected.BlobPartB,actual.BlobPartB);

            Assert.AreEqual(expected.CollectionProperty.Count, actual.CollectionProperty.Count);
            
            BlobList<DummyBlobPartA> collection = expected.CollectionProperty;
            for (int index = 0; index < collection.Count; index++)
            {
                DummyBlobPartA itemExpectedA = collection[index];
                DummyBlobPartA itemActualA = actual.CollectionProperty[index];
                CheckBlobPartAAreEqual(itemActualA, itemExpectedA);
                var collectionB= itemExpectedA.CollectionProperty;
                for (int indexB = 0; indexB < collectionB.Count; indexB++)
                {
                    var itemExpectedB = collectionB[indexB];
                    var itemActualB = itemActualA.CollectionProperty[indexB];
                    CheckBlobPartBAreEqual(itemActualB, itemExpectedB);
                }
            }
        }

        public  void CheckBlobPartBAreEqual(DummyBlobPartB actual, DummyBlobPartB expected)
        {
            Assert.AreEqual(expected.EntityId, actual.EntityId);
            Assert.AreEqual(expected.StatusStorage, actual.StatusStorage);
            Assert.AreEqual(expected.IntProperty, actual.IntProperty);
            Assert.AreEqual(expected.StringProperty, actual.StringProperty);
        }

        public  void CheckBlobPartAAreEqual(DummyBlobPartA actual, DummyBlobPartA expected)
        {
            Assert.AreEqual(expected.EntityId, actual.EntityId);
            Assert.AreEqual(expected.StatusStorage, actual.StatusStorage);
            Assert.AreEqual(expected.IntProperty, actual.IntProperty);
            Assert.AreEqual(expected.StringProperty, actual.StringProperty);
            Assert.AreEqual(expected.CollectionProperty.Count, actual.CollectionProperty.Count);
            CheckBlobPartBAreEqual(expected.BlobPartB, actual.BlobPartB);
        }
    }
}